﻿using System;
using System.Windows;
using System.ComponentModel;
using System.Windows.Threading;

namespace SilverlightCairngorm
{
	/// <summary>
	/// modestyZ: 2008.10
	/// Abstract class that makes INotifyPropertyChanged implementation thread-safe
	/// Any type that serves as DataContext for data binding in a multi-threaded application should derive from this type
	/// </summary>
	public abstract class BindableBase : INotifyPropertyChanged
	{
		//the dispatcher to ensure the PropertyChange event will raised for UI thread to update bound data
		protected Dispatcher currentDispatcher;

		public BindableBase() : this(null) { }

		public BindableBase(Dispatcher uiDispatcher)
		{
			currentDispatcher = uiDispatcher;
		}

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged = delegate { };
		protected void NotifyPropertyChanged(string propertyName)
		{
			//the dispatcher initialization is deferred till the first PropertyChanged event raised
			CheckDispatcher();
			
			//check if we are on the Dispatcher thread if not switch
			if (currentDispatcher.CheckAccess())
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			else
				currentDispatcher.BeginInvoke(new Action<string>(NotifyPropertyChanged), propertyName);
		}

		#endregion

		/// <summary>
		/// check and initialize the UI thread dispatcher when the custructor passed in null dispatcher instance
		/// </summary>
		protected void CheckDispatcher()
		{
			if (null != currentDispatcher)
				return;

			if (Application.Current != null &&
				Application.Current.RootVisual != null &&
				Application.Current.RootVisual.Dispatcher != null)
			{
				currentDispatcher = Application.Current.RootVisual.Dispatcher;
			}
			else // can't get the Dispatcher, throw an exception 
			{
				throw new InvalidOperationException("CheckDispatcher must be invoked after that the RootVisual has been loaded");
			}
		}
	}
}
